/* **********************************************************************************
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This source code is subject to terms and conditions of the Shared Source License
* for DSL Editor PowerToy. A copy of the license can be found in the License.htm file
* at the root of this distribution. If you can not locate the Shared Source License
* for DSL Editor PowerToy, please obtain a copy from: http://www.codeplex.com/dsltreegrideditor/Project/License.aspx.
* By using this source code in any fashion, you are agreeing to be bound by
* the terms of the Shared Source License for DSL Editor PowerToy.
*
* You must not remove this notice, or any other, from this software.
*
* **********************************************************************************/
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Text;
using System.Windows.Forms;

using VSTrid = Microsoft.VisualStudio.VirtualTreeGrid;

using DslEditorPowerToy.Controls.VirtualTreeGrid.Branches;
using DslEditorPowerToy.Controls.VirtualTreeGrid.Validation;

namespace DslEditorPowerToy.Controls.VirtualTreeGrid
{
    public abstract partial class VirtualTreeGridControl : VSTrid.VirtualTreeControl
    {
        private ColumnCollection _columns = null;
        private object _selectedObject = null;
        private VSTrid.IBranch _toggleExpandAsyncBranch = null;
        
        #region Constructors
        public VirtualTreeGridControl()
            : base()
        {
            InitializeComponent();

            #region TreeControl Configurations
            //Configure control for specific purpose
            base.DisplayColumnHeaders = true;
            base.DistinguishFocusedColumn = true;
            base.FullCellSelect = true;
            base.HasButtons = true;
            base.HasRootButtons = false;
            base.HasRootLines = false;
            base.HasHorizontalGridLines = true;
            base.HasGridLines = true;
            base.HasLines = true;
            base.HasVerticalGridLines = false;
            base.HeaderButtons = true;
            base.HeaderImageList = null;
            //base.HeaderControl = null;
            base.HeaderDragDrop = false;
            base.HeaderFullDrag = false;
            //base.ImageDescriptions = null;
            base.ImageList = null;
            base.IsDragSource = false;
            base.LabelEditSupport = VSTrid.VirtualTreeLabelEditActivationStyles.Delayed
                | VSTrid.VirtualTreeLabelEditActivationStyles.ImmediateMouse
                | VSTrid.VirtualTreeLabelEditActivationStyles.ImmediateSelection
                | VSTrid.VirtualTreeLabelEditActivationStyles.Explicit;
            base.MultiColumnHighlight = true;
            base.SelectionMode = SelectionMode.One;
            base.StandardCheckBoxes = false;
            //base.StateImageAccessibleStates = null;
            //base.StateImageDescriptions = null;
            base.StateImageList = null;
            #endregion

            //Create at least one column
            this._columns = new ColumnCollection();
            this._columns.Add(new VSTrid.VirtualTreeColumnHeader(ControlResources.DefaultColumnName, 1.0F));
        }
        #endregion

        #region Public Events
        /// <summary>
        /// Raised when an item is moved in the tree
        /// </summary>
        public event VSTrid.ItemMovedEventHandler ItemMoved;
        /// <summary>
        /// Raised when the visible item count is changed
        /// </summary>
        public event VSTrid.ItemCountChangedEventHandler ItemCountChanged;
        /// <summary>
        /// Raised when an item dataItem has changed
        /// </summary>
        public event VSTrid.DisplayDataChangedEventHandler DisplayDataChanged;
        #endregion

        #region Public Properties

        /// <summary>
        /// Gets/sets the currently selected object
        /// </summary>
        [Browsable(false)]
        public object DataSource
        {
            get
            {
                return this._selectedObject;
            }
            set
            {
                this._selectedObject = value;
                SetDataSource(value);
            }
        }
        /// <summary>
        /// Gets the collection columns for the control
        /// </summary>
        [Browsable(false)]
        public ColumnCollection Columns
        {
            get
            {
                return this._columns;
            }
        }

        #region Overriden Hidden Properties

        [Browsable(false)]
        [ReadOnly(true)]
        public new bool HasRootButtons
        {
            get
            {
                return base.HasRootButtons;
            }
        }
        [Browsable(false)]
        [ReadOnly(true)]
        public new bool HasRootLines
        {
            get
            {
                return base.HasRootLines;
            }
        }
        [Browsable(false)]
        [ReadOnly(true)]
        public new VSTrid.VirtualTreeLabelEditActivationStyles LabelEditSupport
        {
            get
            {
                return base.LabelEditSupport;
            }
        }
        [Browsable(false)]
        [ReadOnly(true)]
        public new VSTrid.ColumnSelectionTransferAction ColumnSelectionTransferAction
        {
            get
            {
                return base.ColumnSelectionTransferAction;
            }
        }
        [Browsable(false)]
        [ReadOnly(true)]
        public new bool DisplayColumnHeaders
        {
            get
            {
                return base.DisplayColumnHeaders;
            }
        }
        [Browsable(false)]
        [ReadOnly(true)]
        public new VSTrid.DragEffectCombinationMode DragEffectCombinationMode
        {
            get
            {
                return base.DragEffectCombinationMode;
            }
        }
        [Browsable(false)]
        [ReadOnly(true)]
        public new bool HeaderButtons
        {
            get
            {
                return base.HeaderButtons;
            }
        }
        [Browsable(false)]
        [ReadOnly(true)]
        public new ImageList HeaderImageList
        {
            get
            {
                return base.HeaderImageList;
            }
        }
        [Browsable(false)]
        [ReadOnly(true)]
        public new VSTrid.VirtualTreeHeaderControl HeaderControl
        {
            get
            {
                return base.HeaderControl;
            }
        }
        [Browsable(false)]
        [ReadOnly(true)]
        public new bool HeaderDragDrop
        {
            get
            {
                return base.HeaderDragDrop;
            }
        }
        [Browsable(false)]
        [ReadOnly(true)]
        public new bool HeaderFullDrag
        {
            get
            {
                return base.HeaderFullDrag;
            }
        }
        [Browsable(false)]
        [ReadOnly(true)]
        public new bool IsDragSource
        {
            get
            {
                return base.IsDragSource;
            }
        }
        [Browsable(false)]
        [ReadOnly(true)]
        public new SelectionMode SelectionMode
        {
            get
            {
                return base.SelectionMode;
            }
        }
        //[Browsable(false)]
        //[ReadOnly(true)]
        //public new bool StandardCheckBoxes
        //{
        //    get
        //    {
        //        return base.StandardCheckBoxes;
        //    }
        //}
        [Browsable(false)]
        [ReadOnly(true)]
        public new System.Collections.IList StateImageAccessibleStates
        {
            get
            {
                return base.StateImageAccessibleStates;
            }
        }
        [Browsable(false)]
        [ReadOnly(true)]
        public new System.Collections.IList StateImageDescriptions
        {
            get
            {
                return base.StateImageDescriptions;
            }
        }
        [Browsable(false)]
        [ReadOnly(true)]
        public new ImageList StateImageList
        {
            get
            {
                return base.StateImageList;
            }
        }
        [Browsable(false)]
        [ReadOnly(true)]
        public new System.Collections.IList ImageDescriptions
        {
            get
            {
                return base.ImageDescriptions;
            }
        }
        [Browsable(false)]
        [ReadOnly(true)]
        public new ImageList ImageList
        {
            get
            {
                return base.ImageList;
            }
        }
        #endregion

        #endregion

        #region Public Methods

        #region Keyboard Navigation Control
        /*
         * We want windows to control keyboard handling as much as possible.
         * However the branch handling the current item may choose to handle 
         * navigation around the branch in their own special way.
         * The basic concept is to allow the branch to handle any keyboard navigation and
         * indicate whether it handled that key or the key required further handling by the control.
         */

        /// <summary>
        /// Handle key presses to hook navigation around the control
        /// The current navigationBranch can choose to handle the key to act appropriately
        /// </summary>
        /// <param name="charCode"></param>
        /// <returns></returns>
        protected override bool ProcessDialogChar(char charCode)
        {
            ProcessKeyReturn handlingAction = ProcessKeyPress(charCode, Control.ModifierKeys);
            if (ProcessKeyReturn.KeyHandledActionOccurred == handlingAction)
                return true;
            else
            {
                //Allow normal windows processing to occur
                bool hasBaseHandledAction = base.ProcessDialogChar(charCode);
                if (true == hasBaseHandledAction)
                    return true;
                else
                {
                    //Prevent further handling if already navigationBranch already handled
                    if (ProcessKeyReturn.KeyHandledNoAction == handlingAction)
                        return true;
                    else
                        //Allow further handling
                        return false;
                }
            }
        }
        /// <summary>
        /// Handle key presses to hook navigation around the control
        /// The current navigationBranch can choose to handle the key to act appropriately
        /// </summary>
        /// <param name="keyData"></param>
        /// <returns></returns>
        protected override bool ProcessDialogKey(Keys keyData)
        {
            ProcessKeyReturn handlingAction = ProcessKeyDown(new KeyEventArgs(keyData));
            if (ProcessKeyReturn.KeyHandledActionOccurred == handlingAction)
                return true;
            else
            {
                //Allow normal windows processing to occur
                bool hasBaseHandledAction = base.ProcessDialogKey(keyData);
                if (true == hasBaseHandledAction)
                    return true;
                else
                {
                    //Prevent further handling if already navigationBranch already handled
                    if (ProcessKeyReturn.KeyHandledNoAction == handlingAction)
                        return true;
                    else
                        //Allow further handling
                        return false;
                }
            }
        }
        /// <summary>
        /// Handle key presses to hook navigation around the control
        /// The current navigationBranch can choose to handle the key to act appropriately
        /// </summary>
        /// <param name="e"></param>
        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (ProcessKeyReturn.NotHandled == ProcessKeyDown(e))
                base.OnKeyDown(e);
            else
                e.Handled = true;
        }
        /// <summary>
        /// Handle key presses to hook navigation around the control
        /// The current navigationBranch can choose to handle the key to act appropriately
        /// </summary>
        /// <param name="e"></param>
        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            if (ProcessKeyReturn.NotHandled == ProcessKeyPress(e.KeyChar, Control.ModifierKeys))
            {
                //base.OnKeyPress(e);
            }
            else
                e.Handled = true;
        }
        protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
        {
            base.OnPreviewKeyDown(e);
        }
        #endregion

        /// <summary>
        /// Expands the specified branch
        /// </summary>
        /// <remarks>Method does this asynchronously, normally after the branch is added to control.</remarks>
        /// <param name="branch"></param>
        public void ToggleExpansionAsync(VSTrid.IBranch branch)
        {
            //Save branch
            this._toggleExpandAsyncBranch = branch;

            //Register idle event
            Application.Idle += new EventHandler(ToggleExpansionAsync_IdleHandler);
        }
        #endregion

        #region Control Handlers
        private void VirtualTreeGrid_MouseUp(object sender, MouseEventArgs e)
        {
            if (MouseButtons.Right == e.Button)
            {
                //Ensure over an item
                VSTrid.VirtualTreeHitInfo hitInfo = this.HitInfo(e.X, e.Y);
                if (hitInfo.Row > -1)
                    this.ContextMenuStrip.Show(this, e.Location);
            }
        }
        private void contextMenuStrip_Opening(object sender, CancelEventArgs e)
        {
            //Ensure selection
            int row = this.CurrentIndex;
            if (row < 0)
                return;

            //Get item information
            VSTrid.VirtualTreeItemInfo itemInfo = this.Tree.GetItemInfo(row, 0, false);
            RowCommandStatus rowCommand = new RowCommandStatus(itemInfo);

            //Enable commands
            this.contextMenuDelete.Enabled = rowCommand.AllowDelete;
            this.contextMenuMoveUp.Enabled = rowCommand.AllowMoveUp;
            this.contextMenuMoveDown.Enabled = rowCommand.AllowMoveDown;
        }
        private void contextMenuStrip_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            //Ensure selection
            int row = this.CurrentIndex;
            if (row < 0)
                return;

            //Get item information
            VSTrid.VirtualTreeItemInfo itemInfo = this.Tree.GetItemInfo(row, 0, false);
            Branches.BranchBase branch = (Branches.BranchBase)itemInfo.Branch;
            RowCommandStatus rowCommand = new RowCommandStatus(itemInfo);

            if (e.ClickedItem == this.contextMenuDelete)
            {
                //Ensure able to perform command
                if (false == rowCommand.AllowDelete)
                    return;

                if (false == branch.RemoveDataItem(itemInfo.Row))
                    MessageBox.Show(ControlResources.ItemDeleteFailed, ControlResources.DeleteFailureTitle, 
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else if (e.ClickedItem == this.contextMenuMoveUp)
            {
                //Ensure able to perform command
                if (false == rowCommand.AllowMoveUp)
                    return;

                if (false == branch.MoveDataItem(itemInfo.Row, (itemInfo.Row - 1)))
                    MessageBox.Show(ControlResources.ItemMoveFailed, ControlResources.MoveFailureTitle,
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else if (e.ClickedItem == this.contextMenuMoveDown)
            {
                //Ensure able to perform command
                if (false == rowCommand.AllowMoveDown)
                    return;

                if (false == branch.MoveDataItem(itemInfo.Row, (itemInfo.Row + 1)))
                    MessageBox.Show(ControlResources.ItemMoveFailed, ControlResources.MoveFailureTitle,
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #region Event Exposing Handling
        void Tree_ItemMoved(object sender, VSTrid.ItemMovedEventArgs e)
        {
            OnItemMoved(e);
        }

        void Tree_ItemCountChanged(object sender, VSTrid.ItemCountChangedEventArgs e)
        {
            OnItemCountChanged(e);
        }

        void Tree_OnDisplayDataChanged(object sender, VSTrid.DisplayDataChangedEventArgs e)
        {
            OnDisplayDataChanged(e);
        }
        #endregion

        #endregion

        #region Protected Methods
        /// <summary>
        /// Raises ItemMoved event
        /// </summary>
        /// <param name="e"></param>
        protected void OnItemMoved(VSTrid.ItemMovedEventArgs e)
        {
            if (null != this.ItemMoved)
                this.ItemMoved(this, e);
        }
        /// <summary>
        /// Raises ItemMoved event
        /// </summary>
        /// <param name="e"></param>
        protected void OnItemCountChanged(VSTrid.ItemCountChangedEventArgs e)
        {
            if (null != this.ItemCountChanged)
                this.ItemCountChanged(this, e);
        }
        /// <summary>
        /// Raises DataDisplayedChanged event
        /// </summary>
        /// <param name="e"></param>
        protected void OnDisplayDataChanged(VSTrid.DisplayDataChangedEventArgs e)
        {
            if (null != this.DisplayDataChanged)
                this.DisplayDataChanged(this, e);
        }
        /// <summary>
        /// Re-initialises the tree control and selects the specied object in tree
        /// </summary>
        protected virtual void SetDataSource(object selectedObject)
        {
            //Initialise the tree
            if (null == this.Tree)
                InitialiseTree();

            //Check value
            if (null == selectedObject)
            {
                //Reset control
                this.Tree = null;
            }
            else
            {
                //Add root and expand first branch
                if (null != this.Tree)
                {
                    try
                    {
                        //Root to select actual item
                        this.SelectObject(this.Tree.Root, selectedObject, (int)VSTrid.ObjectStyle.TrackingObject, 0);
                    }
                    catch (ArgumentOutOfRangeException)
                    {
                        //Ignore exception, as probably cant find object
                        //Perhaps branch data has changed?
                    }
                }
            }
        }
        protected void SetRoot(VSTrid.IBranch rootBranch)
        {
            InitialiseTree();

            //Set and expand root branch
            this.Tree.Root = rootBranch;
            if (null != this.Tree.Root)
                this.Tree.ToggleExpansion(0, 0);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected override void OnSelectionChanged(EventArgs e)
        {
            //Get the current row
            int row = this.CurrentIndex;
            if (row < 0)
                return;

            //Dereference the branch
            VSTrid.VirtualTreeItemInfo itemInfo = this.Tree.GetItemInfo(row, 0, false);
            
            //Notify branch of selection change (of relative row)
            IDataItemBranch itemBranch = itemInfo.Branch as IDataItemBranch;
            if (null != itemBranch)
                itemBranch.OnDataItemSelectionChanged(itemInfo.Row);                

            base.OnSelectionChanged(e);
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Creates the tree and sets up the visual appearance
        /// </summary>
        /// <param name="selectedObject"></param>
        private void InitialiseTree()
        {
            //Validate columns
            if (this._columns.Count < 1)
                throw new InvalidOperationException("The control must define some columns to display.");

            //Configure multi column tree
            this.MultiColumnTree = new VSTrid.MultiColumnTree(this._columns.Count);
            this.SetColumnHeaders(this._columns.GetColumnHeaders(), true);
            this.Tree.OnDisplayDataChanged += new VSTrid.DisplayDataChangedEventHandler(Tree_OnDisplayDataChanged);
            this.Tree.ItemCountChanged += new VSTrid.ItemCountChangedEventHandler(Tree_ItemCountChanged);
            this.Tree.ItemMoved += new VSTrid.ItemMovedEventHandler(Tree_ItemMoved);
        }

        /// <summary>
        /// Handles the idle event for the control, and handles the expansion of the expand branch
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ToggleExpansionAsync_IdleHandler(object sender, EventArgs e)
        {
            //Unregister event
            Application.Idle -= new EventHandler(ToggleExpansionAsync_IdleHandler);

            try
            {
                //Expand branch
                if (null == this._toggleExpandAsyncBranch)
                    return;

                if (null == this.Tree)
                    return;

                ////Gets a visible rows for branch
                int rowCount = this._toggleExpandAsyncBranch.VisibleItemCount;
                for (int rowIndex = 0; rowIndex < rowCount; rowIndex++)
                {
                    //Get the absolute index of the row
                    VSTrid.VirtualTreeCoordinate coordinate = this.Tree.LocateObject(this._toggleExpandAsyncBranch, rowIndex,
                        (int)VSTrid.ObjectStyle.TrackingObject, 0);
                    if (-1 == coordinate.Row)
                        continue;

                    //Toggle row expansion
                    this.Tree.ToggleExpansion(coordinate.Row, 0);
                }
            }
            finally
            {
                //Reset local variable
                this._toggleExpandAsyncBranch = null;
            }
        }

        #region Keyboard Navigation Methods
        /// <summary>
        /// Handles the key handling for the control and current branch
        /// </summary>
        /// <param name="keyPressed"></param>
        /// <param name="modifiers"></param>
        /// <returns></returns>
        internal ProcessKeyReturn ProcessKeyPress(char keyPressed, Keys modifiers)
        {
            //Ensure selection
            if (this.CurrentIndex < 0)
                return ProcessKeyReturn.NotHandled;

            int selectedRowAbsIndex = this.CurrentIndex;
            VSTrid.VirtualTreeItemInfo info = this.Tree.GetItemInfo(selectedRowAbsIndex, 0, false);
            int selectedCellColumnIndex = this.CurrentColumn;
            int selectedRowRelIndex = info.Row;

            //Ensure current navigationBranch handles key navigation
            IKeyNavigationBranch navigationBranch = GetBranchAsKeyNavigator(info.Branch);
            if (navigationBranch == null)
                return ProcessKeyReturn.NotHandled;

            //Allow navigationBranch to handle key
            ProcessKeyResult branchProcessingResult = navigationBranch.ProcessKeyPress(selectedRowRelIndex, selectedCellColumnIndex, keyPressed, modifiers);
            if (true == ProcessKeyNavigationBranchResult(branchProcessingResult, navigationBranch, selectedRowAbsIndex, selectedRowRelIndex, selectedCellColumnIndex))
                return ProcessKeyReturn.KeyHandledActionOccurred;
            else
            {
                if (KeyAction.Handle == branchProcessingResult.Action)
                    return ProcessKeyReturn.KeyHandledNoAction;
                else
                    return ProcessKeyReturn.NotHandled;
            }
        }
        /// <summary>
        /// Handles the key handling for the control and current branch
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        internal ProcessKeyReturn ProcessKeyDown(KeyEventArgs e)
        {
            //Ensure selection
            if (this.CurrentIndex < 0)
                return ProcessKeyReturn.NotHandled;

            int selectedRowAbsIndex = this.CurrentIndex;
            VSTrid.VirtualTreeItemInfo info = this.Tree.GetItemInfo(selectedRowAbsIndex, 0, false);
            int selectedCellColumnIndex = this.CurrentColumn;
            int selectedRowRelIndex = info.Row;

            //Ensure current navigationBranch handles key navigation
            IKeyNavigationBranch navigationBranch = GetBranchAsKeyNavigator(info.Branch);
            if (navigationBranch == null)
                return ProcessKeyReturn.NotHandled;

            // Handle Enter key action explicity, instead of passing to branch
            if ((e.KeyCode == Keys.Enter)
                && (false == this.InLabelEdit))
            {
                /*&& (DoDefaultAction(branch, selectedRowRelIndex))*/
                return ProcessKeyReturn.KeyHandledActionOccurred;
            }

            // Handle Escape key action explicity, instead of passing to branch
            if ((e.KeyCode == Keys.Escape)
                && (true == this.InLabelEdit))
            {
                //Cancel label edit
                this.EndLabelEdit(true);
                return ProcessKeyReturn.KeyHandledActionOccurred;
            }

            // Handle Del key action explicity, instead of passing to branch
            if ((e.KeyCode == Keys.Delete)
                && (false == this.InLabelEdit))
            {
                //Delete selected item
                return ProcessKeyReturn.KeyHandledActionOccurred;
            }
            //Allow navigationBranch to handle key
            ProcessKeyResult branchProcessingResult = navigationBranch.ProcessKeyDown(selectedRowRelIndex, selectedCellColumnIndex, e);
            if (true == ProcessKeyNavigationBranchResult(branchProcessingResult, navigationBranch, selectedRowAbsIndex, selectedRowRelIndex, selectedCellColumnIndex))
                return ProcessKeyReturn.KeyHandledActionOccurred;
            else
            {
                if (KeyAction.Handle == branchProcessingResult.Action)
                    return ProcessKeyReturn.KeyHandledNoAction;
                else
                    return ProcessKeyReturn.NotHandled;
            }
        }
        /// <summary>
        /// Handles the handling of keys from the navigation branch (if any)
        /// </summary>
        /// <param name="processKeyResult"></param>
        /// <param name="navigationBranch"></param>
        /// <param name="absRowIndex"></param>
        /// <param name="relRowIndex"></param>
        /// <param name="cellColumnIndex"></param>
        /// <returns></returns>
        private bool ProcessKeyNavigationBranchResult(ProcessKeyResult processKeyResult, IKeyNavigationBranch navigationBranch, 
            int absRowIndex, int relRowIndex, int cellColumnIndex)
        {
            bool hasActionOccurred = false;
            bool isLabelEditInProgress = this.InLabelEdit;

            //Determine if requires further processing
            switch (processKeyResult.Action)
            {
                case KeyAction.Discard:
                    return true;

                case KeyAction.Process:
                    return false;

                default:
                    break;
            }

            //Check if start edit mode specified
            if (true == processKeyResult.StartLabelEdit)
            {
                //Start control edit mode
                this.InLabelEdit = true;
                return true;
            }

            //Handle label editing navigation
            if (true == isLabelEditInProgress)
            {
                if (true == navigationBranch.IsRowNewPlaceholderRow(relRowIndex))
                {
                    //Unless the user has actually typed some text here,
                    //Note: We treat this as non-edit mode (counter.e, don't restore edit mode after navigation)
                    isLabelEditInProgress = (this.LabelEditControl.Text.Length > 0);
                }
            }

            //The navigationBranch may block deactivation of the edit control,
            //because of an invalid entry, for example. In this case we should just return.
            this.InLabelEdit = false;
            if (true == this.InLabelEdit)
                return false;

            //Expand navigationBranch first, if necesary
            if (true == processKeyResult.ExpandBranch)
            {
                //Assume cellColumnIndex = 0 because branches in this control dont allow expansiions within cells
                if (false == this.Tree.IsExpanded(absRowIndex, 0))
                {
                    //Check branch supports expanding, otherwise we can do nothing 
                    if (false == this.Tree.IsExpandable(absRowIndex, 0))
                        return false;

                    //Expand row
                    this.Tree.ToggleExpansion(absRowIndex, 0);
                    hasActionOccurred = true;
                }
            }

            //Navigate around branches
            VSTrid.TreeNavigation treeDirection = ConvertDirectionToNavigation(processKeyResult.Direction);
            Type branchType = navigationBranch.GetType();
            int oldAbsRowIndex = absRowIndex;

            //Find next cell to navigate to
            bool isEditableColumnFound = false;
            while (false == isEditableColumnFound)
            {
                oldAbsRowIndex = absRowIndex;

                //Check location of cell and direction of navigation
                if ((VSTrid.TreeNavigation.LeftColumn == treeDirection) 
                    && (0 == cellColumnIndex))
                {
                    //Move to end of previous row in same branch
                    absRowIndex--;
                    if (absRowIndex >= 0)
                    {
                        VSTrid.VirtualTreeItemInfo info = Tree.GetItemInfo(absRowIndex, 0, false);
                        cellColumnIndex = GetLastColumnIndex(info.Branch, info.Row);
                    }
                }
                else if ((VSTrid.TreeNavigation.RightColumn == treeDirection)
                    && (cellColumnIndex == GetLastColumnIndex((VSTrid.IBranch)navigationBranch, relRowIndex)))
                {
                    //Move to start of next row in same branch
                    absRowIndex++;
                    cellColumnIndex = 0;
                }
                else
                {
                    // search is not restricted to a particular cellColumnIndex, so we translate up/down to 
                    // left/right search to give a better experience.
                    if (VSTrid.TreeNavigation.Up == treeDirection)
                    {
                        //Move to next left cell in branch
                        absRowIndex--;
                        treeDirection = VSTrid.TreeNavigation.LeftColumn;
                        if (absRowIndex >= 0)
                        {
                            // handle jagged cellColumnIndex cases
                            VSTrid.VirtualTreeItemInfo info = this.Tree.GetItemInfo(absRowIndex, 0, false);
                            if (null != info.Branch)
                            {
                                int columnIndex = GetLastColumnIndex(info.Branch, info.Row);
                                if (cellColumnIndex > columnIndex)
                                    cellColumnIndex = columnIndex;
                            }
                        }
                    }
                    else if (VSTrid.TreeNavigation.Down == treeDirection)
                    {
                        //Move to next right cell in branch
                        absRowIndex++;
                        treeDirection = VSTrid.TreeNavigation.RightColumn;
                        if (absRowIndex < this.Tree.VisibleItemCount)
                        {
                            // handle jagged cellColumnIndex cases
                            VSTrid.VirtualTreeItemInfo info = this.Tree.GetItemInfo(absRowIndex, 0, false);
                            if (null != info.Branch)
                            {
                                int columnIndex = GetLastColumnIndex(info.Branch, info.Row);
                                if (cellColumnIndex > columnIndex)
                                    cellColumnIndex = columnIndex;
                            }
                        }
                    }
                }

                //Ensure not outside bounds of items in tree
                if ((absRowIndex < 0) 
                    || (absRowIndex >= this.Tree.VisibleItemCount))
                    break;

                VSTrid.VirtualTreeCoordinate coordinate = new VSTrid.VirtualTreeCoordinate(absRowIndex, cellColumnIndex);
                if (absRowIndex == oldAbsRowIndex)
                {
                    //If the above didn't result in any navigation, ask the tree to do it itself.
                    coordinate = this.Tree.GetNavigationTarget(treeDirection, absRowIndex, 
                        cellColumnIndex, this.ColumnPermutation);
                }
                //Check if the coordinate is now a valid cell
                if (true == coordinate.IsValid)
                {
                    absRowIndex = coordinate.Row;
                    cellColumnIndex = coordinate.Column;
                    
                    //Check if changed rows
                    if (oldAbsRowIndex != absRowIndex)
                    {
                        //Retrieve new row info
                        VSTrid.VirtualTreeItemInfo info = this.Tree.GetItemInfo(absRowIndex, 0, false);

                        //Check if navigating outside current branch
                        if ((null != navigationBranch) 
                            && (navigationBranch != info.Branch))
                        {
                            //Check if we can't navigate to other branch
                            if (true == processKeyResult.LocalBranchNavigationOnly)
                                break;
                        }

                        navigationBranch = GetBranchAsKeyNavigator(info.Branch);
                        if (null != navigationBranch)
                            branchType = navigationBranch.GetType();
                        else
                            branchType = null;
                        relRowIndex = info.Row;
                    }

                    //Check if permits navigation to specific branch type
                    if ((null != navigationBranch)
                        && ((null == processKeyResult.BranchType)
                            || (branchType == processKeyResult.BranchType)
                            || (true == branchType.IsSubclassOf(processKeyResult.BranchType))))
                    {
                        //Check if expandable or branch supports navigation
                        if ((navigationBranch is IKeyNavigationBranch)
                            || ((navigationBranch as VSTrid.IBranch).IsExpandable(relRowIndex, cellColumnIndex)))
                        {
                            isEditableColumnFound = true;
                            break;
                        }
                    }
                }
                else
                    break;
            }

            //Execute the navigation
            if (true == isEditableColumnFound)
            {
                int currentIndex = this.CurrentIndex;
                if (absRowIndex != currentIndex)
                {
                    if (currentIndex != -1)
                    {
                        // currentIndex may be -1 if we toggled expansion,
                        // but in that case the base control will take care
                        // of the redraw
                        // TODO : is this a bug in the control?  shouldn't selection
                        // be restored after toggling expansion?
                        InvalidateItem(currentIndex);
                    }

                    this.CurrentIndex = absRowIndex;
                    hasActionOccurred = true;
                }

                if (cellColumnIndex != CurrentColumn)
                {
                    this.CurrentColumn = cellColumnIndex;
                    hasActionOccurred = true;
                }
            }

            if (true == isLabelEditInProgress)
                this.InLabelEdit = isLabelEditInProgress;

            return hasActionOccurred;
        }
        /// <summary>
        /// Helper to get the Key Navigator for the current branch
        /// </summary>
        /// <param name="branch"></param>
        /// <returns></returns>
        private IKeyNavigationBranch GetBranchAsKeyNavigator(VSTrid.IBranch branch)
        {
            if (branch is IKeyNavigationBranch)
                return branch as IKeyNavigationBranch;
            else
                return null;
        }
        /// <summary>
        /// Converts from navigation direction to tree navigation direction
        /// </summary>
        /// <param name="navigationDirection"></param>
        /// <returns></returns>
        private VSTrid.TreeNavigation ConvertDirectionToNavigation(NavigationDirection navigationDirection)
        {
            switch (navigationDirection)
            {
                case NavigationDirection.Left:
                    return VSTrid.TreeNavigation.LeftColumn;

                case NavigationDirection.Right:
                    return VSTrid.TreeNavigation.RightColumn;

                case NavigationDirection.Up:
                    return VSTrid.TreeNavigation.Up;

                case NavigationDirection.Down:
                    return VSTrid.TreeNavigation.Down;

                default:
                    return VSTrid.TreeNavigation.None;
            }
        }
		/// <summary>
		/// Returns the last column index for the given branch/row.  
        /// Accounts for jagged columns
		/// </summary>
        private int GetLastColumnIndex(VSTrid.IBranch branch, int rowRelIndex)
		{
            if (branch is VSTrid.IMultiColumnBranch)
            {
                VSTrid.IMultiColumnBranch multiBranch = branch as VSTrid.IMultiColumnBranch;
			    if (null != multiBranch)
			    {
                    if ((branch.Features & VSTrid.BranchFeatures.JaggedColumns) != 0)
					    return multiBranch.GetJaggedColumnCount(rowRelIndex) - 1;
				    else
					    return multiBranch.ColumnCount - 1;
			    }
            }

			return 0;
		}
        /// <summary>
        /// Invalidate a specific row in the tree and forces a redraw on that row
        /// </summary>
        public void InvalidateItem(int absRowIndex)
        {
            if ((absRowIndex < 0) 
                || ((null != this.Tree) && (absRowIndex >= this.Tree.VisibleItemCount)))
                throw new ArgumentOutOfRangeException("absRowIndex");

            NativeMethods.Rectangle rect;
            NativeMethods.SendMessage(this.Handle, NativeMethods.LB_GETITEMRECT, absRowIndex, out rect);
            NativeMethods.RedrawWindow(this.Handle, ref rect, IntPtr.Zero, 
                (NativeMethods.RedrawWindowFlags.Invalidate | NativeMethods.RedrawWindowFlags.Erase));
        }
        #endregion

        #endregion

        #region Child Classes
        private class RowCommandStatus
        {
            private VSTrid.VirtualTreeItemInfo _rowInfo;
            private Branches.DataItemFlags _itemFlags = Branches.DataItemFlags.None;
            private Branches.DataItemFlags _nextItemFlags = Branches.DataItemFlags.None;
            private Branches.DataItemFlags _prevItemFlags = Branches.DataItemFlags.None;

            #region Constructors
            public RowCommandStatus(VSTrid.VirtualTreeItemInfo rowInfo)
            {
                ArgumentValidator.ValidateNotNull("rowInfo", rowInfo);
                this._rowInfo = rowInfo;

                //Get branch for row
                Branches.BranchBase branch = (Branches.BranchBase)rowInfo.Branch;
                this._itemFlags = branch.GetDataItemFlags(rowInfo.Row);
                this._nextItemFlags = branch.GetDataItemFlags(rowInfo.Row + 1);
                this._prevItemFlags = branch.GetDataItemFlags(rowInfo.Row - 1);
            }
            #endregion

            #region Public Properties
            public VSTrid.VirtualTreeItemInfo RowInfo
            {
                get
                {
                    return this._rowInfo;
                }
            }
            public bool AllowDelete
            {
                get
                {
                    return ((this._itemFlags & Branches.DataItemFlags.CanDelete) > 0);
                }
            }
            public bool AllowMoveUp
            {
                get
                {
                    return ((this._itemFlags & Branches.DataItemFlags.CanMove) > 0)
                    && ((this._prevItemFlags & Branches.DataItemFlags.CanMove) > 0);
                }
            }
            public bool AllowMoveDown
            {
                get
                {
                    return ((this._itemFlags & Branches.DataItemFlags.CanMove) > 0)
                    && ((this._nextItemFlags & Branches.DataItemFlags.CanMove) > 0);
                }
            }
            #endregion
        }
        #endregion
    }
}
